/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.designtime.generator.defaultvalue.mapping.method;

import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bff.designtime.generator.VmGeneratorContext;
import com.inspur.edp.bff.designtime.generator.common.GenUtils;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.generator.DotNetToJavaStringHelper;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.GspViewObject;

import java.util.ArrayList;
import java.util.HashMap;

import lombok.var;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;

public class ConvertDefaultValueFormVoGenerator extends ClassMethodGenerator {

    private static final String ParamVoElementCode = "voElementCode";
    private static final String ParamVoValue = "voValue";
    private static final String ParamBeDefaultValue = "beDefaultValue";
    private static final String ParamBeValue = "beValue";
    private VmGeneratorContext vmGeneratorContext;
    private GspViewObject voObject;
    private GspBizEntityObject beObject;

    public ConvertDefaultValueFormVoGenerator(VmGeneratorContext vmGeneratorContext,
                                              GspViewObject voObject, GspBizEntityObject beObject) {
        this.vmGeneratorContext = vmGeneratorContext;
        this.voObject = voObject;
        this.beObject = beObject;
    }


    //region MethodBasicInfos
    @Override
    protected boolean getIsOverride() {
        return true;
    }

    @Override
    protected java.util.ArrayList<ModifierKeyword> getAccessModifiers() {
        return new java.util.ArrayList<ModifierKeyword>(java.util.Arrays.asList(
                new ModifierKeyword[]{ModifierKeyword.PROTECTED_KEYWORD}));
    }

    @Override
    protected String getMethodName() {
        return "convertDefaultFormVo";
    }

    @Override
    protected java.util.ArrayList<ParameterInfo> getParameterCollection() {
        ParameterInfo tempVar = new ParameterInfo();
        tempVar.setParamType(new TypeInfo(String.class));
        tempVar.setParamName(ParamVoElementCode);
        ParameterInfo tempVar2 = new ParameterInfo();
        tempVar2.setParamType(new TypeInfo(Object.class));
        tempVar2.setParamName(ParamVoValue);
        ParameterInfo tempVar3 = new ParameterInfo();
        tempVar3.setParamType(getMapType());
        tempVar3.setParamName(ParamBeDefaultValue);
        return new java.util.ArrayList<ParameterInfo>(
                java.util.Arrays.asList(new ParameterInfo[]{tempVar, tempVar2, tempVar3}));
    }

    private TypeInfo getMapType() {
        var typeInfo = new TypeInfo(HashMap.class);
        typeInfo.setIsParameterizedType(true);
        typeInfo.getArguments().add(new TypeInfo(String.class));
        typeInfo.getArguments().add(new TypeInfo(Object.class));
        return typeInfo;
    }

    @Override
    protected TypeInfo getReturnType() {
        return new TypeInfo(void.class);
    }
    //endregion MethodBasicInfos

    //region MethodBody
    @Override
    protected Block buildMethodBody() {
        VariableDeclarationStatement beValueStatement = getBeValueDeclaration();
        SwitchStatement result = getSwitchBlock();
        for (IGspCommonField field : voObject.getContainElements()) {
            GspViewModelElement item = (GspViewModelElement) field;
            if (item.getIsVirtual()) {
                continue;
            }
            if (DotNetToJavaStringHelper.isNullOrEmpty(item.getMapping().getTargetObjId()))
            //TODO 异常封装
            {
                throw new RuntimeException(
                        item.getCode() + " 的Mapping字段ID不允许为空");
            }

            if (!item.getMapping().getTargetMetadataId().equals(
                    vmGeneratorContext.getVm().getMapping().getTargetMetadataId()
            )) {
                continue;
            }

            Object tempVar = beObject.findElement(item.getMapping().getTargetObjId());
            GspBizEntityElement entityElement = (GspBizEntityElement) (
                    (tempVar instanceof GspBizEntityElement) ? tempVar : null);
            if (entityElement == null) {
                continue;
            }
            addElementMapBlock(item, entityElement, result);
        }
        Block block = ast.newBlock();
        block.statements().add(beValueStatement);
        block.statements().add(result);
        return block;
    }

    private VariableDeclarationStatement getBeValueDeclaration() {
        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName(ParamBeValue));
        variableDeclarationFragment.setInitializer(ast.newNullLiteral());
        VariableDeclarationStatement variableDeclarationStatement = ast
                .newVariableDeclarationStatement(variableDeclarationFragment);
        variableDeclarationStatement.setType(new TypeInfo(Object.class).getType(ast, vmGeneratorContext.getEntityContext()));
        return variableDeclarationStatement;
    }

    private SwitchStatement getSwitchBlock() {
        SwitchStatement switchStatement = ast.newSwitchStatement();
        switchStatement.setExpression(ast.newSimpleName(ParamVoElementCode));
        return switchStatement;
    }


    //region Element
    private void addElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement,
                                    SwitchStatement result) {
        if (vmElement.getIsUdt()) {
            //TODO 暂不支持UDT
            return;
        }
        switch (vmElement.getObjectType()) {
            case DynamicProp:
                return;
            case None:
                addBasicElementMapBlock(beElement, result);
                break;
            case Association:
                addAssElementMapBlock(vmElement, beElement, result);
                break;
            case Enum:
                addEnumElementMapBlock(vmElement, beElement, result);
                break;
            default:
                throw new RuntimeException(
                        "错误的类型" + vmElement.getObjectType().toString());
        }
    }

    private void addBasicElementMapBlock(GspBizEntityElement beElement,
                                         SwitchStatement result) {
        SwitchCase caseExpr = ast.newSwitchCase();
        StringLiteral caseLiteral = ast.newStringLiteral();
        caseLiteral.setLiteralValue(beElement.getLabelID());
        caseExpr.setExpression(caseLiteral);
        result.statements().add(caseExpr);
        MethodInvocation defaultValueAddInvocation = ExpressionUtils.getMethodInvocation(ast,ParamBeDefaultValue,"put");
        StringLiteral labelIdLiteral = ast.newStringLiteral();
        labelIdLiteral.setLiteralValue(beElement.getLabelID());
        defaultValueAddInvocation.arguments().add(labelIdLiteral);
        defaultValueAddInvocation.arguments().add(ast.newSimpleName(ParamVoValue));
        result.statements().add(ast.newExpressionStatement(defaultValueAddInvocation));
        result.statements().add(ast.newBreakStatement());
    }


    //region AddAssElementMapBlock
    private void addAssElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement,
                                       SwitchStatement result) {
        java.util.ArrayList<Statement> statementSyntax = new java.util.ArrayList<Statement>();
        getIfNullStatement(statementSyntax, beElement, vmElement);
        getAddValueStatement(beElement);
        statementSyntax.add(ast.newBreakStatement());
        SwitchCase switchCaseExpr = ast.newSwitchCase();
        StringLiteral labelIdLiteral = ast.newStringLiteral();
        labelIdLiteral.setLiteralValue(vmElement.getLabelID());
        switchCaseExpr.setExpression(labelIdLiteral);
        result.statements().add(switchCaseExpr);
        Block block = ast.newBlock();
        block.statements().addAll(statementSyntax);
        result.statements().add(block);
    }


    //region GetIfNullStatement
    private void getIfNullStatement(java.util.ArrayList<Statement> statementSyntax,
                                    GspBizEntityElement beElement, GspViewModelElement vmElement) {
        IfStatement ifStatement = ast.newIfStatement();
        InfixExpression ifCondition = ast.newInfixExpression();
        ifCondition.setLeftOperand(ast.newSimpleName(ParamVoValue));
        ifCondition.setOperator(Operator.EQUALS);
        ifCondition.setRightOperand(ast.newNullLiteral());
        ifStatement.setExpression(ifCondition);
        Block thenBlock = ast.newBlock();
        Assignment assignment = ast.newAssignment();
        assignment.setLeftHandSide(ast.newSimpleName(ParamBeValue));
        assignment.setOperator(Assignment.Operator.ASSIGN);
        assignment.setRightHandSide(ast.newNullLiteral());
        thenBlock.statements().add(ast.newExpressionStatement(assignment));
        ifStatement.setThenStatement(thenBlock);
        Block elseBlock = ast.newBlock();
        elseBlock.statements().addAll(getElseStatements(beElement, vmElement));
        ifStatement.setElseStatement(elseBlock);
        statementSyntax.add(ifStatement);
    }


    //region GetElseStatements
    private ArrayList<Statement> getElseStatements(GspBizEntityElement beElement,
                                                   GspViewModelElement vmElement) {
        java.util.ArrayList<Statement> elseStatements = new java.util.ArrayList<Statement>();
        addGetVMAssStatement(elseStatements, vmElement);
        newBeAssStatement(elseStatements, beElement);
        assPropMapBlock(vmElement, beElement, elseStatements);
        setBEValueStatement(elseStatements);
        elseStatements.add(getAddStatement(beElement));
        return elseStatements;
    }

    private ExpressionStatement getAddStatement(GspBizEntityElement beElement) {
        MethodInvocation addInvocation = ast.newMethodInvocation();
        addInvocation.setExpression(ast.newSimpleName(ParamBeDefaultValue));
        addInvocation.setName(ast.newSimpleName("put"));
        StringLiteral labelIdLiteral = ast.newStringLiteral();
        labelIdLiteral.setLiteralValue(beElement.getLabelID());
        addInvocation.arguments().add(labelIdLiteral);
        addInvocation.arguments().add(ast.newSimpleName(ParamBeValue));
        return ast.newExpressionStatement(addInvocation);
    }

    private void addGetVMAssStatement(java.util.ArrayList<Statement> elseStatements,
                                      GspViewModelElement vmElement) {
        String vmAssoTypeName = vmElement.getAssociationTypeName();
        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName("vmAss"));
        CastExpression castExpression = ast.newCastExpression();
        castExpression.setExpression(ast.newSimpleName(ParamVoValue));
        castExpression.setType(ast.newSimpleType(ast.newSimpleName(vmAssoTypeName)));
        variableDeclarationFragment.setInitializer(castExpression);
        VariableDeclarationStatement variableDeclarationStatement = ast
                .newVariableDeclarationStatement(variableDeclarationFragment);
        variableDeclarationStatement.setType(ast.newSimpleType(ast.newSimpleName(vmAssoTypeName)));
        elseStatements.add(variableDeclarationStatement);
    }


    private void newBeAssStatement(java.util.ArrayList<Statement> elseStatements,
                                   GspBizEntityElement beElement) {
        TypeInfo assType = GenUtils
                .GetBEAssoType(vmGeneratorContext, beElement, "");
        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName("beAss"));
        ClassInstanceCreation creation = ast.newClassInstanceCreation();
        creation.setType(assType.getType(ast, vmGeneratorContext.getEntityContext()));
        variableDeclarationFragment.setInitializer(creation);
        VariableDeclarationStatement variableDeclarationStatement = ast
                .newVariableDeclarationStatement(variableDeclarationFragment);
        variableDeclarationStatement.setType(assType.getType(ast, vmGeneratorContext.getEntityContext()));
        elseStatements.add(variableDeclarationStatement);

    }

    private void assPropMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement,
                                 java.util.ArrayList<Statement> elseStatements) {
        for (IGspCommonField field : vmElement.getChildAssociations().get(0)
                .getRefElementCollection()) {
            GspViewModelElement item = (GspViewModelElement) field;
            GspBizEntityElement beAssElement = (GspBizEntityElement)
                    beElement.getChildAssociations().get(0).getRefElementCollection()
                            .getItem(item.getMapping().getTargetObjId());
            MethodInvocation setPropInvocation = ast.newMethodInvocation();
            setPropInvocation.setName(ast.newSimpleName("set" + beAssElement.getLabelID()));
            setPropInvocation.setExpression(ast.newSimpleName("beAss"));
            MethodInvocation getPropInvocation = ast.newMethodInvocation();
            getPropInvocation.setName(ast.newSimpleName("get" + item.getLabelID()));
            getPropInvocation.setExpression(ast.newSimpleName("vmAss"));
            setPropInvocation.arguments().add(getPropInvocation);
            elseStatements.add(ast.newExpressionStatement(setPropInvocation));
        }

        MethodInvocation setPropInvocation = ast.newMethodInvocation();
        setPropInvocation.setName(ast.newSimpleName("set" + beElement.getLabelID()));
        setPropInvocation.setExpression(ast.newSimpleName("beAss"));
        MethodInvocation getPropInvocation = ast.newMethodInvocation();
        getPropInvocation.setName(ast.newSimpleName("get" + vmElement.getLabelID()));
        getPropInvocation.setExpression(ast.newSimpleName("vmAss"));
        setPropInvocation.arguments().add(getPropInvocation);
        elseStatements.add(ast.newExpressionStatement(setPropInvocation));
    }

    private void setBEValueStatement(java.util.ArrayList<Statement> elseStatements) {
        Assignment assignment = ast.newAssignment();
        assignment.setLeftHandSide(ast.newSimpleName(ParamBeValue));
        assignment.setOperator(Assignment.Operator.ASSIGN);
        assignment.setRightHandSide(ast.newSimpleName("beAss"));
        elseStatements.add(ast.newExpressionStatement(assignment));
    }

    //endregion GetElseStatements

    //endregion GetIfNullStatement

    private void getAddValueStatement(GspBizEntityElement beElement) {
        MethodInvocation addInvocation = ast.newMethodInvocation();
        addInvocation.setName(ast.newSimpleName("put"));
        addInvocation.setExpression(ast.newSimpleName(ParamBeDefaultValue));
        StringLiteral labelIdLiteral = ast.newStringLiteral();
        labelIdLiteral.setLiteralValue(beElement.getLabelID());
        addInvocation.arguments().add(labelIdLiteral);
        addInvocation.arguments().add(ast.newSimpleName(ParamBeValue));
    }

    //endregion AddAssElementMapBlock

    private void addEnumElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement,
                                        SwitchStatement result) {
        TypeInfo enumType = GenUtils
                .getBeEnumType(vmGeneratorContext, beElement, "");

        String varName = "vmEnumValue" + vmElement.getLabelID();
        SwitchCase switchCase = ast.newSwitchCase();
        StringLiteral caseLiteral = ast.newStringLiteral();
        caseLiteral.setLiteralValue(vmElement.getLabelID());
        switchCase.setExpression(caseLiteral);
        result.statements().add(switchCase);

        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName(varName));
        CastExpression castVmEnum = ast.newCastExpression();
        castVmEnum.setType(ast.newSimpleType(ast.newSimpleName(vmElement.getEnumTypeName())));
        castVmEnum.setExpression(ast.newSimpleName(ParamVoValue));
        variableDeclarationFragment.setInitializer(castVmEnum);
        VariableDeclarationStatement variableDeclarationStatement = ast
                .newVariableDeclarationStatement(variableDeclarationFragment);
        variableDeclarationStatement
                .setType(ast.newSimpleType(ast.newSimpleName(vmElement.getEnumTypeName())));
        result.statements().add(variableDeclarationStatement);



        MethodInvocation addInvocation = ast.newMethodInvocation();
        addInvocation.setName(ast.newSimpleName("put"));
        addInvocation.setExpression(ast.newSimpleName(ParamBeDefaultValue));
        StringLiteral labelIdArg = ast.newStringLiteral();
        labelIdArg.setLiteralValue(beElement.getLabelID());
        addInvocation.arguments().add(labelIdArg);
        //SexEnum.forValue(vmEnumValueSex.getValue())
        var methodInvocation =ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("forValue"));
        methodInvocation.setExpression(ast.newName(enumType.getTypeFullName()));
        var argInvocation = ExpressionUtils.getMethodInvocation(ast,varName,"getValue" );
        methodInvocation.arguments().add(argInvocation);
//        CastExpression castInt = ast.newCastExpression();
//        castInt.setType(new TypeInfo("int").getType(ast));
//        castInt.setExpression(ast.newSimpleName(varName));
//        CastExpression castEnum = ast.newCastExpression();
//        castEnum.setType(enumType.getType(ast, vmGeneratorContext.getEntityContext()));
//        castEnum.setExpression(castInt);
        addInvocation.arguments().add(methodInvocation);
        result.statements().add(ast.newExpressionStatement(addInvocation));

        result.statements().add(ast.newBreakStatement());
    }
    //endregion

    //endregion
}
